
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "oslib/messagetrans.h"
#include "oslib/osfile.h"
#include "oslib/osmodule.h"
#include "oslib/territory.h"

#include "defs.h"
#include "log.h"
#include "utils.h"

typedef struct
{
  int         errnum;
  const char *errmess;
} errdesc;


static const errdesc errors[] =
{
  { error_MEMSHORT, "MemShort:Not enough memory available" },
  { error_TEMPLATE_NOT_FOUND, "NoTemplate" },
  { error_CANT_FIND_VRAM, "XFindVRAM" },
  { error_NOT_ENOUGH_VRAM, "XEnoughVRAM" },
  { error_CANT_FIND_GCARD, "XFindGCard" },
  { error_CANT_START_URI, "CantStartURI" },
  { error_UNRECOGNISED_PARAMETER, "UKParam" },

  /* terminator */
  { -1, "" },
};
static os_error unknown_error = { 1234, "Unspecified error" };

#define NOF_ERROR_BLOCKS 4

static os_error errbuf[NOF_ERROR_BLOCKS];
static int next_errbuf = 0;


messagetrans_control_block msgs;
static bool msgs_open = false;
static void *msgs_buf = NULL;

territory_t territory = territory_UK;
int territory_len;
char territory_name[TERRITORY_MAX_LEN] = "UK";
static char msgs_filename[FILENAME_LENGTH] = "<PicoDrive$Dir>.Resources.UK.Messages";


/* If our Messages file isn't already open, then try to open it */

os_error *messages_open(const char *dir, int dirlen)
{
  fileswitch_object_type obj_type;
  messagetrans_file_flags flags;
  char *p = msgs_filename;
  os_error *err;
  int size;

  if (msgs_open)
    return NULL;

  if (!xterritory_number(&territory) &&
      xterritory_number_to_name(territory, territory_name, sizeof(territory_name)))
  {
    /* use default then */
    memcpy(territory_name, "UK", 3);
    territory = territory_UK;
  }
  territory_name[sizeof(territory_name)-1] = '\0';
  territory_len = strlen(territory_name);

  memcpy(p, dir, dirlen); p += dirlen; *p++ = '.';
  memcpy(p, "Resources.", 10); p += 10;
  memcpy(p, territory_name, territory_len); p += territory_len;
  memcpy(p, ".Messages", 10);

  if (xosfile_read_stamped(msgs_filename, &obj_type, NULL, NULL, NULL, NULL, NULL) ||
      obj_type == osfile_NOT_FOUND)
  {
    memcpy(territory_name, "UK", 3);
    territory = territory_UK;
    territory_len = 3;
    memcpy(&msgs_filename[dirlen + 1], "Resources.UK.Messages", 22);
  }

  err = xmessagetrans_file_info(msgs_filename, &flags, &size);
  if (!err)
  {
    if (!(flags & messagetrans_DIRECT_ACCESS))
    {
      err = xosmodule_alloc(size, &msgs_buf);
      if (err) return err;
    }
    err = xmessagetrans_open_file(&msgs, msgs_filename, (char*)msgs_buf);
    if (!err) msgs_open = true;
  }
  return err;
}

void messages_close(void)
{
  if (msgs_open)
  {
    os_error *err = xmessagetrans_close_file(&msgs);
    assert(!err);
    msgs_open = false;
  }
  if (msgs_buf)
  {
    os_error *err = xosmodule_free(msgs_buf);
    assert(!err);
    msgs_buf = NULL;
  }
}

os_error *messages_lookup(char *buf, int size, const char *msgtok,
                          const char *arg0, const char *arg1, const char *arg2, const char *arg3)
{
  messagetrans_control_block *m = msgs_open ? &msgs : NULL;
  return xmessagetrans_lookup(m, msgtok, buf, size, arg0, arg1, arg2, arg3, NULL, NULL);
}

os_error *translate_errdesc(const errdesc *ed, const char *arg0, const char *arg1, const char *arg2, const char *arg3)
{
  messagetrans_control_block *m;
  const char *text = ed->errmess;
  os_error *te, *e;
  int errlen;

  e = &errbuf[next_errbuf];
  if (++next_errbuf >= NOF_ERROR_BLOCKS) next_errbuf = 0;
  e->errnum = ed->errnum;

  while (*text && *text != ':') text++;
  errlen = text - ed->errmess;
  memcpy(e->errmess, ed->errmess, errlen);
  e->errmess[errlen] = '\0';

  /* if we fail(ed) to open our messages file, then MessageTrans will
     just use the default string in our error table above */
  m = msgs_open ? &msgs : NULL;

  te = xmessagetrans_error_lookup(e, m, NULL, 0, arg0, arg1, arg2, arg3);
  if (!te || te->errnum == error_MESSAGE_TRANS_TOKEN_NOT_FOUND)
  {
    /* MessageTrans doesn't allow us to provide a default translation
       to ErrorLookup - so we do it ourselves (but note that there's
       no parameter substitution!); just in case we can't get to our
       Messages file for some reason (the token name is at least a clue) */

    if (*text == ':')
      errlen = strlen(++text);
    else
      text = ed->errmess;

    errlen = min(errlen, 251);
    memcpy(e->errmess, text, errlen);
    e->errmess[errlen] = '\0';
    e->errnum = ed->errnum;
    return e;
  }
  return te;
}

os_error *lookup_error4(bits err, const char *arg0, const char *arg1, const char *arg2, const char *arg3)
{
  const errdesc *ed = errors;
  while (ed->errnum != -1)
  {
    if (err == ed->errnum)
      return translate_errdesc(ed, arg0, arg1, arg2, arg3);
    ed++;
  }
  unknown_error.errnum = err;
  sprintf(unknown_error.errmess, "Unspecified error %.8X", err);
  return &unknown_error;
}

os_error *lookup_error(bits err)
  { return lookup_error4(err, NULL, NULL, NULL, NULL); }
os_error *lookup_error1(bits err, const char *arg0)
  { return lookup_error4(err, arg0, NULL, NULL, NULL); }
os_error *lookup_error2(bits err, const char *arg0, const char *arg1)
  { return lookup_error4(err, arg0, arg1, NULL, NULL); }
os_error *lookup_error3(bits err, const char *arg0, const char *arg1, const char *arg2)
  { return lookup_error4(err, arg0, arg1, arg2, NULL); }

